package com.android.webserver;

import java.io.IOException;

import android.content.ContentResolver;
import android.content.Context;
import android.database.Cursor;
import android.net.Uri;
import android.util.Log;

import com.android.webserver.db.WebApplication;
import com.android.webserver.security.FramingPrevention;
import com.android.webserver.security.HSTS;
import com.android.webserver.security.Origin;
import com.android.webserver.security.SCSP;
import com.android.webserver.security.URLScan;
import com.android.webserver.tornado.HTTP;
import com.android.webserver.tornado.HTTPException;
import com.android.webserver.tornado.Request;
import com.android.webserver.tornado.Response;
import com.android.webserver.tornado.Tornado;
import com.android.webserver.util.Base64;
import com.android.webserver.util.InternalPages;

/**
 * @author Baptiste GOURDIN
 *
 */
public class WebAppDispatcher
{
	/**
	 * Dispatch the HTTP Request to the good provider
	 * 1) The Request is first verified by the URLScan.
	 * 2) Check if it is an internal page of the web server (javascript, authentication).
	 * 3) PRE validation : check the validity of the request
	 * 4) Send the query to the web application provider.
	 * 5) POST validation : add security mechanisms
	 * @param context WebServerService context
	 * @param request	Incoming HTTP Request
	 * @param response HTTP Response
	 * @return True if the response has been filled correctly, false if an error occurred.
	 * @throws HTTPException If an error occurred
	 */
	public static boolean dispatch(Context context, Request request,
			Response response) throws HTTPException
	{
		if (!URLScan.scanRequest(request))
		{
			Log.i("WebServer", "****** BAD request : URL scan failed ******");
			response.setHTTPStatusCode(HTTP.BAD_REQUEST);
			return true;
		}

		String fullPath = request.getFullPath();
		String appPath = request.getAppPath();

		if (InternalPages.isInternalPage(context ,request, response))
			return true;
		
		// Retrieve the Web Application Provider from the request path
		WebApplication webApp = Tornado.androidService.db
				.getWebApplicationFromPath(appPath);
		
		if ((webApp != null) && (webApp.getEnabled()))
		{
			if (!Tornado.androidService.sessions.isAuthentified(webApp, request, response))
				return true;

			// CSRF token check
			// return = false means the request (non-entry point) is corrupted
			// by the absence of a valid CSRF token
			if (webApp.getCsrfStatus())
				if (!CSRFPrevention.verifyRequest(request, webApp, response)) {
					response.setHTTPStatusCode(HTTP.BAD_REQUEST);
					return true;
				}

			// Origin, Referer header check
			if (webApp.getOriginHeaderStatus())
				if (!Origin.verifyRequest(request, response)) {
					response.setHTTPStatusCode(HTTP.FORBIDDEN);
					return true;
				}	

			// Perform HSTS validation and redirect to https if required
//			if (webApp.getHstsStatus())
//				if (!HSTS.verifyRequest(request, response)) {
//					return true;
//				}
			
			ContentResolver cr = context.getContentResolver();

			Cursor c = cr.query(Uri.parse("content://" + webApp.getProvider()
					+ fullPath), null, null, new String[] { request.getURI(),
					request.getType(), request.getRawHeaders(), request.getRawBody() },
					null);

			// Get the page generated by the web application
			if ((c == null) || (c.getCount() != 1))
			{
				Log.e("WebServer", (c == null) ? "WebApplication Fatal Error"
						: ("Cannot find app path : " + fullPath));
				return false;
			}
			else
				c.moveToFirst();

			try
			{
				// Get the Headers
				response.setSerializedHeader(c.getString(0));
				// Get the Content
				if (response.isHTML)
					response.HTMLContent = c.getString(1);
				else
					response.RawContent = Base64.decode(c.getString(1));
				response.setHTTPStatusCode(c.getInt(2));

				// Add framing protection (frame-busting code + X-FRAME-OPTION)
				if (webApp.getXframeStatus())
					FramingPrevention.verifyResponse(response, null);
				if (webApp.getScspStatus())
					SCSP.validateCSP(request, response);

			}
			catch (IOException e)
			{
				Log
						.e("WebServer",
								"WebAppDispatcher dispatch : error base64-decoding the reponse body");
				e.printStackTrace();
			}

			c.close();
			return true;

		}
		return false;
	}

}
